/*
 * $Id: CellContext.java 3778 2010-09-07 10:10:49Z kleopatra $
 *
 * Copyright 2006 Sun Microsystems, Inc., 4150 Network Circle,
 * Santa Clara, California 95054, U.S.A. All rights reserved.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 * 
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 */
package org.jdesktop.swingx.renderer;

import java.awt.Color;
import java.awt.Font;
import java.io.Serializable;

import javax.swing.Icon;
import javax.swing.JComponent;
import javax.swing.UIManager;
import javax.swing.border.Border;
import javax.swing.border.EmptyBorder;

/**
 * Encapsulates a snapshop of cell content and default display context for usage
 * by a <code>ComponentProvider</code>.
 * <p>
 * 
 * One part is the super-set of properties that's traditionally passed into the
 * core renderers' (Table-, List-, Tree-) getXXCellRendererComponent. Raw
 * properties which define the context are
 * 
 * <ul>
 * <li>selected
 * <li>focused
 * <li>expanded
 * <li>leaf
 * </ul>
 * 
 * Similarl to a ComponentAdapter, the properties are a super-set of those for a
 * concrete component type. It's up to sub-classes (once the generics will be
 * removed, until then the DefaultXXRenderers - PENDING JW: undecided - even
 * after the generics removal, the param list in the subclasses are the same)
 * fill any reasonable defaults for those not applicable to the specific
 * component context.
 * 
 * With those raw properties given, a CellContext looks up and returns dependent
 * visual properties as appropriate for the concrete component. Typically, they
 * are taken from the component if supported, or requested from the UIManager.
 * Dependent properties are
 * 
 * <ul>
 * <li>foreground and background color
 * <li>border
 * <li>icon (relevant for trees only)
 * <li>editable
 * </ul>
 *
 * For a backdoor, the cell location (in horizontal and vertical view
 * coordinates) and the originating component is accessible as well. Note that
 * they are not necessarily valid for the "life" component. It's not recommended
 * to actually use them. If needed, that's probably a sign the api is lacking
 * :-)
 * <p>
 * 
 * 
 * <ul>
 * 
 * <li>PENDING: still incomplete? how about Font?
 * <li>PENDING: protected methods? Probably need to open up - derived properties
 * should be accessible in client code.
 * </ul>
 * 
 * @author Jeanette Winzenburg
 */
public class CellContext implements Serializable {

	/** the default border for unfocused cells. */
	protected static Border noFocusBorder = new EmptyBorder(1, 1, 1, 1);

	/** ?? the default border for unfocused cells. ?? */
	private static final Border SAFE_NO_FOCUS_BORDER = new EmptyBorder(1, 1, 1, 1);

	/**
	 * Returns the shared border for unfocused cells.
	 * <p>
	 * PENDING: ?? copied from default renderers - why is it done like this?
	 * 
	 * @return the border for unfocused cells.
	 */
	private static Border getNoFocusBorder() {
		if (System.getSecurityManager() != null) {
			return SAFE_NO_FOCUS_BORDER;
		} else {
			return noFocusBorder;
		}
	}

	/**
	 * PENDING JW: maybe make this a WeakReference? Would be a more robust fix
	 * for Issue #1040-swingx.
	 */
	protected transient JComponent component;

	/**
	 * PENDING JW: maybe make this a WeakReference? Would be a more robust fix
	 * for Issue #1040-swingx.
	 */
	protected transient Object value;

	protected transient int row;

	protected transient int column;

	protected transient boolean selected;

	protected transient boolean focused;

	protected transient boolean expanded;

	protected transient boolean leaf;

	protected transient boolean dropOn;

	// --------------------------- install context

	/**
	 * Sets the state of the cell's context. Convenience method for subclasses.
	 * 
	 * @param value
	 *            the content value of the cell
	 * @param row
	 *            the cell's row index in view coordinates
	 * @param column
	 *            the cell's column index in view coordinates
	 * @param selected
	 *            the cell's selected state
	 * @param focused
	 *            the cell's focused state
	 * @param expanded
	 *            the cell's expanded state
	 * @param leaf
	 *            the cell's leaf state
	 */
	protected void installState(Object value, int row, int column, boolean selected, boolean focused, boolean expanded,
			boolean leaf) {
		this.value = value;
		this.row = row;
		this.column = column;
		this.selected = selected;
		this.focused = focused;
		this.expanded = expanded;
		this.leaf = leaf;
	}

	/**
	 * Replaces the value of this cell context with the given parameter and
	 * returns the replaced value.
	 * 
	 * @param value
	 *            the new value of the cell context
	 * @return the replaced value of the cell context
	 */
	public Object replaceValue(Object value) {
		Object old = getValue();
		this.value = value;
		return old;
	}

	// -------------------- accessors of installed state

	/**
	 * Returns the component the cell resides on, may be null. Subclasses are
	 * expected to override and return the component type they are handling.
	 * 
	 * @return the component the cell resides on, may be null.
	 */
	public JComponent getComponent() {
		return component;
	}

	/**
	 * Returns the value of the cell as set in the install.
	 * 
	 * @return the content value of the cell.
	 */
	public Object getValue() {
		return value;
	}

	/**
	 * Returns the cell's row index in view coordinates as set in the install.
	 * 
	 * @return the cell's row index.
	 */
	public int getRow() {
		return row;
	}

	/**
	 * Returns the cell's column index in view coordinates as set in the
	 * install.
	 * 
	 * @return the cell's column index.
	 */
	public int getColumn() {
		return column;
	}

	/**
	 * Returns the selected state as set in the install.
	 * 
	 * @return the cell's selected state.
	 */
	public boolean isSelected() {
		return selected;
	}

	/**
	 * Returns the focused state as set in the install.
	 * 
	 * @return the cell's focused state.
	 */
	public boolean isFocused() {
		return focused;
	}

	/**
	 * Returns the expanded state as set in the install.
	 * 
	 * @return the cell's expanded state.
	 */
	public boolean isExpanded() {
		return expanded;
	}

	/**
	 * Returns the leaf state as set in the install.
	 * 
	 * @return the cell's leaf state.
	 */
	public boolean isLeaf() {
		return leaf;
	}

	// -------------------- accessors for derived state
	/**
	 * Returns the cell's editability. Subclasses should override to return a
	 * reasonable cell-related state.
	 * <p>
	 * 
	 * Here: false.
	 * 
	 * @return the cell's editable property.
	 */
	public boolean isEditable() {
		return false;
	}

	/**
	 * Returns the icon. Subclasses should override to return a reasonable
	 * cell-related state.
	 * <p>
	 * 
	 * Here: <code>null</code>.
	 * 
	 * @return the cell's icon.
	 */
	public Icon getIcon() {
		return null;
	}

	/**
	 * Returns a boolean indicating if the cell is a drop location with any of
	 * the dropOn modes. It's up to subclasses to implement.
	 * <p>
	 * 
	 * Here: false.
	 * 
	 * @return true if the current cell is a drop location with any of the
	 *         dropOn modes, false otherwise
	 */
	protected boolean isDropOn() {
		return dropOn;
	}

	/**
	 * Returns the foreground color of the renderered component or null if the
	 * component is null
	 * <p>
	 * 
	 * PENDING: fallback to UI properties if comp == null?
	 * 
	 * @return the foreground color of the rendered component.
	 */
	protected Color getForeground() {
		if (isDropOn()) {
			return getSelectionForeground();
		}
		return getComponent() != null ? getComponent().getForeground() : null;
	}

	/**
	 * Returns the background color of the renderered component or null if the
	 * component is null
	 * <p>
	 * 
	 * PENDING: fallback to UI properties if comp == null?
	 * 
	 * @return the background color of the rendered component.
	 */
	protected Color getBackground() {
		if (isDropOn()) {
			return getSelectionBackground();
		}
		return getComponent() != null ? getComponent().getBackground() : null;
	}

	/**
	 * Returns the default selection background color of the renderered
	 * component. Typically, the color is LF specific. It's up to subclasses to
	 * look it up. Here: returns null.
	 * <p>
	 * 
	 * PENDING: return UI properties here?
	 * 
	 * @return the selection background color of the rendered component.
	 */
	protected Color getSelectionBackground() {
		return null;
	}

	/**
	 * Returns the default selection foreground color of the renderered
	 * component. Typically, the color is LF specific. It's up to subclasses to
	 * look it up. Here: returns null.
	 * <p>
	 * 
	 * PENDING: return UI properties here?
	 * 
	 * @return the selection foreground color of the rendered component.
	 */
	protected Color getSelectionForeground() {
		return null;
	}

	/**
	 * Returns the default focus border of the renderered component. Typically,
	 * the border is LF specific.
	 * 
	 * @return the focus border of the rendered component.
	 */
	protected Border getFocusBorder() {
		Border border = null;
		if (isSelected()) {
			border = UIManager.getBorder(getUIKey("focusSelectedCellHighlightBorder"));
		}
		if (border == null) {
			border = UIManager.getBorder(getUIKey("focusCellHighlightBorder"));
		}
		return border;
	}

	/**
	 * Returns the default border of the renderered component depending on cell
	 * state. Typically, the border is LF specific.
	 * <p>
	 * 
	 * Here: returns the focus border if the cell is focused, the context
	 * defined no focus border otherwise.
	 * 
	 * @return the default border of the rendered component.
	 */
	protected Border getBorder() {
		if (isFocused()) {
			return getFocusBorder();
		}
		Border border = UIManager.getBorder(getUIKey("cellNoFocusBorder"));
		return border != null ? border : getNoFocusBorder();
	}

	/**
	 * Returns the default focused foreground color of the renderered component.
	 * Typically, the color is LF specific.
	 * 
	 * @return the focused foreground color of the rendered component.
	 */
	protected Color getFocusForeground() {
		return UIManager.getColor(getUIKey("focusCellForeground"));
	}

	/**
	 * Returns the default focused background color of the renderered component.
	 * Typically, the color is LF specific.
	 * 
	 * @return the focused background color of the rendered component.
	 */
	protected Color getFocusBackground() {
		return UIManager.getColor(getUIKey("focusCellBackground"));
	}

	protected Color getDropCellForeground() {
		return UIManager.getColor(getUIKey("dropCellForeground"));
	}

	protected Color getDropCellBackground() {
		return UIManager.getColor(getUIKey("dropCellBackground"));
	}

	// ----------------------- convenience

	/**
	 * Convenience method to build a component type specific lookup key for the
	 * UIManager.
	 * 
	 * @param key
	 *            the general part of the key
	 * @return a composed key build of a component type prefix and the input.
	 */
	protected String getUIKey(String key) {
		return getUIPrefix() + key;
	}

	/**
	 * Returns the component type specific prefix of keys for lookup in the
	 * UIManager. Subclasses must override, here: returns the empty String.
	 * 
	 * @return the component type specific prefix.
	 */
	protected String getUIPrefix() {
		return "";
	}

	/**
	 * Returns the Font of the target component or null if no component
	 * installed.
	 * 
	 * @return
	 */
	protected Font getFont() {
		return getComponent() != null ? getComponent().getFont() : null;
	}

	public String getCellRendererName() {
		return getUIPrefix() + "cellRenderer";
	}

}
